package roborescue.examples;

import jason.RoborescueEnv;
import jason.asSyntax.Structure;

import java.rmi.RemoteException;
import java.text.DecimalFormat;
import robocode.rescue.RobotInfo;
import robocode.rescue.features.Defesa;
import robocode.rescue.interfaces.RMIRobotInterface;

public class TimeETeamEnv extends RoborescueEnv {

    private final int numRobos = 5;
    private RobotInfo[] robos;
    private RobotInfo[] robosInimigos;

    private double[] posicoesAnterioresX = new double[4];
    private double[] posicoesAnterioresY = new double[4];
    private double[] posicaoInterseccaoX = new double[2];
    private double[] posicaoInterseccaoY = new double[2];
    private double[] interseccoes = new double[5];
    private double[][] pontosInterseccoes = new double[4][3];
    private double[] distancia = new double[5];
    private int[] atacantes = new int[3];
    private int[] defensores = new int[3];
    private double distanciaRobo1Refem;
    public Defesa def;
    private int i = 0;

    final double PI = Math.PI;

    // Para inicializacoes necessarias
    @Override
    public void setup() {
        robos = new RobotInfo[numRobos];
        robosInimigos = new RobotInfo[numRobos];
    }

    @Override
    public boolean executeAction(String ag, Structure action) {
        try {
            mainLoop();
            Thread.sleep(20);
        } catch (RemoteException ex) {
            ex.printStackTrace();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        return true;
    }

    public void mainLoop() throws RemoteException {

        robos = getServerRef().getMyTeamInfo(myTeam);
        robosInimigos = getServerRef().getEnemyTeamInfo(myTeam);

        RMIRobotInterface[] teamRef = getTeamRef();

        defensores[0] = 1;//setta o robo 1 como defensor
        defensores[1] = 4;//setta o robo 4 como defensor

        calculaDistancia();//Define os atacantes
        //realizaEstrategia();//Executa a logica principal
        paraCasoInterceptado();//O robo "fica parado" caso tenha interceptado

    }

    @Override
    public void end() {
        try {
            super.getEnvironmentInfraTier().getRuntimeServices().stopMAS();
        } catch (Exception ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }

    public static void main(String[] args) {
        TimeETeamEnv team = new TimeETeamEnv();
        team.init(new String[]{"TimeE", "localhost"});
        team.setup();
        while (true) {
            try {
                team.mainLoop();
                Thread.sleep(20);
            } catch (RemoteException ex) {
                ex.printStackTrace();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

    public double[] enviaNaPosicaoDoInimigo(RobotInfo[] roboInimigos, int k) {
        double[] posicaoInimigo = new double[2];
        posicaoInimigo[0] = roboInimigos[k].getX();
        posicaoInimigo[1] = roboInimigos[k].getY();

        return posicaoInimigo;
    }

    public int[] calculaDistancia() throws RemoteException {//verifica a distancia de todos os robos inimigos em relação ao nosso refém
        int k = 0;
        for (int j = 1; j < 5; j++) {
            distancia[j] = getRange(robosInimigos[j].getX(), robosInimigos[j].getY(), robos[0].getX(), robos[0].getY());
            atacantes[2] = (int) distancia[j];
            //System.out.println("Distancia do robo "+j+" :" +new DecimalFormat("0.00").format(distancia[j]));
            if (distancia[j] > 1000) {//verifica quais robos inimigos são atacantes
                atacantes[k] = j;
                k++;
            }
        }
        return atacantes;
    }
    
    void paraCasoInterceptado() throws RemoteException{
        RMIRobotInterface[] teamRef = getTeamRef();
        boolean verificador = true;
        for (int k = 0; k < 2 ; k++){
            for (int l = 0; l < 2 ; l++){
                if(getRange(robosInimigos[k].getX(),robosInimigos[k].getY(), robos[l].getX(),robos[l].getY())<=60){
                    //goTo(robos[l].getX(),robos[l].getY(),robos[l],teamRef[l]);
                    verificador = false;
                }
            }
        }if(verificador == true){
            realizaEstrategia();
        }
    }

    void realizaEstrategia() throws RemoteException {
        
        RMIRobotInterface[] teamRef = getTeamRef();
        if (atacantes[0] != 0 || atacantes[1] != 0) {
            int j = 0;
            for (int k = 0; k < 2; k++) {
                for (int l = 0; l < 2; l++) {
                    def = new Defesa(robos, robosInimigos, atacantes[k], defensores[l], posicoesAnterioresX[k], posicoesAnterioresY[k]);
                    def.start();
                    try {
                        def.join();
                        interseccoes = def.getValores();
                    } catch (InterruptedException ie) {
                    }
                    pontosInterseccoes[j][0] = interseccoes[0];
                    pontosInterseccoes[j][1] = interseccoes[1];
                    if (l == 1) {
                        posicoesAnterioresX[k] = interseccoes[2];
                        posicoesAnterioresY[k] = interseccoes[3];
                    }
                    j++;
                }
            }
            /*System.out.println("----------------------------------------------------------------------------");
             System.out.println("Robo " + atacantes[0] + " com ponto de interseccao: " + pontosInterseccoes[0][0] + " , " + pontosInterseccoes[0][1]);
             System.out.println("Robo " + atacantes[0] + " com ponto de interseccao: " + pontosInterseccoes[1][0] + " , " + pontosInterseccoes[1][1]);
             System.out.println("Robo " + atacantes[1] + " com ponto de interseccao: " + pontosInterseccoes[2][0] + " , " + pontosInterseccoes[2][1]);
             System.out.println("Robo " + atacantes[1] + " com ponto de interseccao: " + pontosInterseccoes[3][0] + " , " + pontosInterseccoes[3][1]);*/

            /*for (int m = 0; m < 4; m++) {
                if (pontosInterseccoes[m][0] > 2200) {//caso esteja no lado direito
                    pontosInterseccoes[m][0] = 1208;
                }
                if (pontosInterseccoes[m][0] < 1200) {//caso esteja no lado direito
                    pontosInterseccoes[m][0] = 1208;
                }
                /*if (pontosInterseccoes[m][0] < 200) {//caso esteja no lado esquerdo
                 pontosInterseccoes[m][0] = 1208;
                 }
                 if (pontosInterseccoes[m][0] > 1200) {//caso esteja no lado esquerdo
                 pontosInterseccoes[m][0] = 1208;
                 }
            }*/
            if (robosInimigos[0].getVelocity() > 0) {
                verificaRefem();
            } else {
                if (getRange(robosInimigos[atacantes[0]].getX(), robosInimigos[atacantes[0]].getY(), robos[defensores[0]].getX(), robos[defensores[0]].getY())
                        < getRange(robosInimigos[atacantes[1]].getX(), robosInimigos[atacantes[1]].getY(), robos[defensores[0]].getX(), robos[defensores[0]].getY())) {
                    goTo(pontosInterseccoes[0][0], pontosInterseccoes[0][1], robos[defensores[0]], teamRef[defensores[0]]);
                } else {
                    goTo(pontosInterseccoes[1][0], pontosInterseccoes[1][1], robos[defensores[0]], teamRef[defensores[0]]);
                }
                if (i > 50) {
                    if (getRange(robosInimigos[atacantes[0]].getX(), robosInimigos[atacantes[0]].getY(), robos[defensores[1]].getX(), robos[defensores[1]].getY())
                            < getRange(robosInimigos[atacantes[1]].getX(), robosInimigos[atacantes[1]].getY(), robos[defensores[1]].getX(), robos[defensores[1]].getY())) {
                        goTo(pontosInterseccoes[2][0], pontosInterseccoes[2][1], robos[defensores[1]], teamRef[defensores[1]]);
                    } else {
                        goTo(pontosInterseccoes[3][0], pontosInterseccoes[3][1], robos[defensores[1]], teamRef[defensores[1]]);
                    }

                }
            }
            i++;
        }
    }

    void verificaRefem() throws RemoteException {
        RMIRobotInterface[] teamRef = getTeamRef();
        distanciaRobo1Refem = getRange(robosInimigos[atacantes[0]].getX(), robosInimigos[atacantes[0]].getY(), robosInimigos[0].getX(), robosInimigos[0].getY());
        if (distanciaRobo1Refem <= 160) {
            System.out.println(robosInimigos[atacantes[0]].getName() + " está com o refem");
            for (int l = 0; l < 2; l++) {
                def = new Defesa(robos, robosInimigos, atacantes[0], defensores[l], robosInimigos[0].getX(), robosInimigos[0].getY());
                def.start();
                try {
                    def.join();
                    interseccoes = def.getValores();
                } catch (InterruptedException ie) {
                }
                goTo(interseccoes[0], interseccoes[1], robos[defensores[l]], teamRef[defensores[l]]);
            }
        } else {
            System.out.println(robosInimigos[atacantes[1]].getName() + " está com o refem");
            for (int l = 0; l < 2; l++) {
                def = new Defesa(robos, robosInimigos, atacantes[1], defensores[l], robosInimigos[0].getX(), robosInimigos[0].getY());
                def.start();
                try {
                    def.join();
                    interseccoes = def.getValores();
                } catch (InterruptedException ie) {
                }
                goTo(interseccoes[0], interseccoes[1], robos[defensores[l]], teamRef[defensores[l]]);
            }
        }
    }

    void goTo(double x, double y, RobotInfo robo, RMIRobotInterface roboInt) throws RemoteException {
        double dist = 40;
        double angle = Math.toDegrees(absbearing(robo.getX(), robo.getY(), x, y));
        double r = turnTo(angle, robo, roboInt);
        roboInt.setAhead(dist * r);
        roboInt.execute();
    }

    /**
     * Gira o menor ângulo para o robô ficar "heading", então retorna a direção
     * que o robô precisa movimentar-se.
     */
    int turnTo(double angle, RobotInfo robo, RMIRobotInterface roboInt) throws RemoteException {
        double ang;
        int dir;
        ang = normaliseBearing(robo.getHeading() - angle);
        if (ang > 90) {
            ang -= 180;
            dir = -1;
        } else if (ang < -90) {
            ang += 180;
            dir = -1;
        } else {
            dir = 1;
        }
        roboInt.setTurnLeft(ang);
        return dir;
    }

    /**
     * Retorna a distância entre dois pontos.
     */
    double getRange(double x1, double y1, double x2, double y2) {
        double x = x2 - x1;
        double y = y2 - y1;
        double range = Math.sqrt(x * x + y * y);
        return range;
    }

    /**
     * Retorna a relação absoluta entre as coordenadas x e y
     *
     */
    public double absbearing(double x1, double y1, double x2, double y2) {

        double xo = x2 - x1;
        double yo = y2 - y1;
        double h = getRange(x1, y1, x2, y2);
        if (xo > 0 && yo > 0) {
            return Math.asin(xo / h);
        }
        if (xo > 0 && yo < 0) {
            return Math.PI - Math.asin(xo / h);
        }
        if (xo < 0 && yo < 0) {
            return Math.PI + Math.asin(-xo / h);
        }
        if (xo < 0 && yo > 0) {
            return 2.0 * Math.PI - Math.asin(-xo / h);
        }
        return 0;
    }

    /**
     * Se uma relação não está dentro do intervalo de-pi a pi, altera para
     * fornecer o menor ângulo
     *
     */
    double normaliseBearing(double ang) {
        if (ang > PI) {
            ang -= 2 * PI;
        }
        if (ang < -PI) {
            ang += 2 * PI;
        }
        return ang;
    }
}
